/****************************************************************************
 * FILE NAME: 		adc.c
 * 
 * PROJECT NAME: 	EGR330_Project
 * TEAM MEMBERS: 	Kevin Vermeer
 * 					Brandon Ross
 *					Tyler Selk
 *
 * 
 * CREATED: 		31 July 2011 (Brandon Ross)
 * LAST MODIFIED: 		
 * 
 * DESCRIPTION:		Handles functions necessary to operate ADC peripheral
 ****************************************************************************/

#include <avr/io.h>
#include <inttypes.h>

#include "adc.h"
#include "generic.h"
#include "hw_profile.h"

/***** LOCAL FUNCTIONS *****/

/*
 * FUNCTION NAME:	ADC_setChannel
 * INPUTS:			MUXX (uint8_t)
 * OUTPUTS:			None
 * DESCRIPTION:		Sets ADC input channel
 */
static void ADC_setChannel(uint8_t MUXX)
{
	
	ADMUX = setRegister(ADMUX, ADC_MASK_MUXX, MUXX, ADC_POS_MUXX);
}

/*
 * FUNCTION NAME: 	ADC_setVREF
 * INPUTS:			REFSX
 * OUTPUTS:			None
 * DESCRIPTION:		Sets ADC Voltage Reference
 */
static void ADC_setVREF(uint8_t REFSX)
{
	ADMUX = setRegister(ADMUX, ADC_MASK_REFSX, REFSX, ADC_POS_REFSX);
}

/*
 * FUNCTION NAME:	ADC_setPrescaler
 * INPUTS:			ADPSX (uint8_t)
 * OUTPUTS:			None
 * DESCRIPTION:		Sets ADC Clock Prescale Division Factor
 */
static void ADC_setPrescaler(uint8_t ADPSX)
{
	ADCSRA = setRegister(ADCSRA, ADC_MASK_ADPSX, ADPSX, ADC_POS_ADPSX);
}

/*
 * FUNCTION NAME:	ADC_disableDigitalInputs
 * INPUTS:			ADCSXD (uint8_t)
 * OUTPUTS:			None
 * DESCRIPTION:		Indicates ADC pins and disables digital inputs
 */
static void ADC_disableDigitalInputs(uint8_t ADCXD)
{
	DIDR0 = setRegister(DIDR0, ADC_MASK_ADCXD, ADCXD, ADC_POS_ADCXD);
}

/*
 * FUNCTION NAME:	ADC_setAutoTrigger
 * INPUTS:			ADTSX (uint8_t)
 * OUTPUTS:			None
 * DESCRIPTION:		Sets ADC Auto Trigger Source
 */
static void ADC_setAutoTrigger(uint8_t ADTSX)
{
	ADCSRB = setRegister(ADCSRB, ADC_MASK_ADTSX, ADTSX, ADC_POS_ADTSX);
}

/*
 * FUNCTION NAME:	ADC_enable
 * INPUTS:			None
 * OUTPUTS:			None
 * DESCRIPTION:		Sets ADC Enable Bit
 */
static void ADC_enable(void)
{
	ADCSRA = setBit(ADCSRA, ADC_BIT_ENABLE);
}

/*
 * FUNCTION NAME:	ADC_startConversion
 * INPUTS:			None
 * OUTPUTS:			None
 * DESCRIPTION:		Sets ADC Start Conversion Bit
 */
static void ADC_startConversion(void)
{
	ADCSRA = setBit(ADCSRA, ADC_BIT_START);
}

/*
 * FUNCTION NAME:	ADC_checkCompletion
 * INPUTS:			None
 * OUTPUTS:			FALSE if ADIF is 0
 *					TRUE if ADIF is 1
 * DESCRIPTION:		Checks whether ADC Interrupt Flag is set
 */
static uint8_t ADC_checkCompletion(void)
{
	return (isBitSet(ADCSRA, ADC_BIT_DONE));
}

/*
 * FUNCTION NAME:	ADC_waitUntilDone
 * INPUTS:			None
 * OUTPUTS:			None
 * DESCRIPTION:		Waits for Conversion to Finish
 */
static void ADC_waitUntilDone(void)
{
	while(ADC_checkCompletion() == FALSE);
}

/*
 * FUNCTION NAME:	ADC_clearFlag
 * INPUTS:			None
 * OUTPUTS:			None
 * DESCRIPTION:		Clears ADIF (set-to-clear)
 */
static void ADC_clearFlag(void)
{
	ADCSRA = setBit(ADCSRA, ADC_BIT_DONE);
}

/*
 * FUNCTION NAME:	ADC_checkRightAdjustment
 * INPUTS:			None
 * OUTPUTS:			FALSE if ADLAR is 1 (Left-Adjusted Result)
 *					TRUE if ADLAR is 0 (Right-Adjusted Result)
 * DESCRIPTION:		Checks whether ADC Interrupt Flag is set
 */
static uint8_t ADC_checkRightAdjustment(void)
{
	return(isBitClear(ADMUX, ADC_BIT_ADLAR));
}

/*
 * FUNCTION NAME:	ADC_read
 * INPUTS:			None
 * OUTPUTS:			reading (adc_reading_t)
 * DESCRIPTION:		Reads ADCL and ADCH registers
 */
static adc_reading_t ADC_read(void)
{
	adc_reading_t reading;

	if(ADC_checkRightAdjustment() == TRUE)
	{
		reading = (ADCH<<8)|ADCL;
	}
	else
	{
		reading = ADCH;
	}

	return reading;
}

/***** EXTERNAL FUNCTIONS *****/

/*
 * FUNCTION NAME:	ADC_intialize
 * INPUTS:			None
 * OUTPUTS:			None
 * DESCRIPTION:		Initializes the ADC peripheral
 */
void ADC_initialize(void)
{
	// Disables all ADC pin digital inputs
	ADC_disableDigitalInputs(ADC_INPUTS_ALL);

	// Sets auto trigger to free running mode
	ADC_setAutoTrigger(ADC_AUTOTRIG_FREERUN);

	// Sets prescale clock to 128 division factor
	// 8 MHz -> 62.5 kHz
	ADC_setPrescaler(ADC_PRESCALE_DF128);

	// Sets voltage reference to AVCC
	ADC_setVREF(ADC_VREF_AVCC);

	// Enables ADC
	ADC_enable();
}

/*
 * FUNCTION NAME:	readCurrent
 * INPUTS:			CURRX (uint8_t)
 * OUTPUTS:			current (adc_reading_t)
 * DESCRIPTION:		Reads ADC given input
 */
adc_reading_t readCurrent(uint8_t CURRX)
{
	adc_reading_t current;

	// Sets ADC input channel
	ADC_setChannel(CURRX);

	// Starts conversion
	ADC_startConversion();

	// Waits until finished
	ADC_waitUntilDone();

	// Reads current measurement
	current = ADC_read();

	// Resets flag
	ADC_clearFlag();

	return(current);
}	
